# -*- coding: utf-8 -*-
"""
Lib. Model
Modul for single and relational table models
"""

#---------------------------------------------------------------------------
# Copyright (c) 2014 Ahmad Ghulam Zakiy <ghulam.zakiy@gmail.com>
# License: GPL3
#
# This file is part of SimpleStore
#
# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
#---------------------------------------------------------------------------

from PySide import QtCore, QtSql
import math
import config
from lib import logger


class baseModel(object):
    """
    Table Model Base
    """

    # Table things
    tableName = None
    queryFilter = ''  
    defaultQuery = ''
    model = None
    # Berisi nama-nama kolom sebuah tabel
    record = None
    # Jumlah baris hasil query
    rowCount = 0
    
    # Stats
    tableIsSet = False
    tableIsLoaded = False
    tablePagingInitLoaded = False
    pagingEnabled = False

    # Logging
    log = None

    # Paging
    page = dict()
    page['table_paging_limit'] = max(config.application['table_paging_limit'], 1) #table_paging_limit
    page['current'] = 1 #currentPage
    page['count'] = 0 #pageCount
    page['first'] = 0 #firstPage
    page['last'] = 0 #lastPage
    
    
    def __init__(self, database):
        self.db = database
        # Signal
        self.signal = tableSignal()
        self.log = logger.log()


    def setTable(self, tableName):

        if self.db.isValidTable(tableName):
            self.tableName = tableName
            self.tableIsSet = True
            self.model.setTable(self.tableName)
            self.model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
            return True
        else:
            return False


    def setPaging(self, paging):
        self.pagingEnabled = paging


    def setDefaultQuery(self):
        query = self.lastExecutedQuery()
        queryLength = len(query)
        limitPos = query.find('LIMIT')

        if (limitPos > -1) & (limitPos > queryLength/2):
            self.defaultQuery = query[:limitPos].strip()
        else:
            self.defaultQuery = query


    def setFilter(self, filter = ''):
        # Memfilter data dari tabel.
        # Catatan: Fungsi ini akan langsung dijalankan bila perintah select() pernah dipanggil sebelumnya,
        #          bila tidak, maka eksekusi menunggu select() setelahnya.        
        
        self.model.setFilter(filter)
        # Periksa apakah ada query pernah dijalankan sebelumnya
        if self.model.query().isActive() is not True:
            return self.loadTable()
        else:
            self.rowCount = self.model.rowCount()
            self.record = self.model.record()
            self.queryFilter = self.model.filter()
            self.setDefaultQuery()
            self.loadPage(self.page['first'])
            return True


    def lastExecutedQuery(self):
        if isinstance(self.model, QtSql.QSqlTableModel) & self.model.query().isActive():
            self.log.logMe(title="Last query", text=self.model.query().lastQuery())
            return self.model.query().lastQuery()
        else:
            self.log.logMe(title="Query", text="No query were run")
            return False


    def setTableHeaders(self):
        """
        # Catatan: Definisi nama field tidak berada dalam model, tapi terletak di dalam database,
        #          begitu juga dengan column hiding pengaturan berada dalam view.
        for i in range(0, self.record.count()):
           self.model.setHeaderData( i, QtCore.Qt.Horizontal, self.record.fieldName(i) )
           print 'setting up header "%s" at "%s", done!' % (self.record.fieldName(i), i)
        """
        # Sebenarnya query secara otomatis akan men-set header dengan sendirinya
        pass


    def loadTable(self):
        if self.model.select() == True:
            self.rowCount = self.model.rowCount()
            self.record = self.model.record()
            self.loadPage(self.page['first'])
            return True
        else:
            self.log.logMe(title="Load table", text="Error")
            self.log.logMe(title="Dump", text=self.model.query().lastError().text())
            return False


    def isPagingActive(self):
        if self.pagingEnabled & self.rowCount > 0:
            return True
        else:
            return False


    def loadModel(self):
        if self.loadTable():
            self.setTableHeaders()
            self.setDefaultQuery()
            return True
        else:
            return False


    def reloadModel(self):
        # Reload query asal
        self.model.reset()
        self.model.setFilter('')
        self.model.setQuery(self.db.Query(self.defaultQuery))        
        self.loadTable()
        self.setTableHeaders()


    def loadPage(self, pageNo = 1):
        if self.isPagingActive():
            self.page['count'] = int(math.ceil(float(self.rowCount) / self.page['table_paging_limit']))
            self.page['first'] = min(1, self.page['count'])
            self.page['last'] = max(1, self.page['count'])
            self.page['current'] = max(pageNo, 1)
            self.page['current'] = min(self.page['current'], self.page['count'])
            
            self.offset = int((self.page['current'] - 1) * self.page['table_paging_limit'])

            lastQuery = self.lastExecutedQuery()

            lastQueryLength = len(lastQuery)
            limitPos = lastQuery.find('LIMIT')

            if (limitPos > -1) & (limitPos > lastQueryLength/2):
                  prevQuery = lastQuery[:limitPos].strip()
            else:
              prevQuery = lastQuery

            self.limitQueryStr = prevQuery + ' LIMIT %s, %s' % (self.offset, self.page['table_paging_limit'])

            self.model.setQuery(self.db.Query(self.limitQueryStr))

            self.signal.tableLoaded.emit(self.page)



class tableModel(baseModel):
    """
       Non-relational Table Model
    """

    def __init__(self, database):
        super(tableModel, self).__init__(database)
        self.model = QtSql.QSqlTableModel()



class queryModel(baseModel):
    """
      Query model
    """

    def __init__(self, database):
        super(queryModel, self).__init__(database)
        self.model = QtSql.QSqlTableModel()
        self.query = QtSql.QSqlQuery()


    def setTable(self, tableName):
        if self.db.isValidTable(tableName):
            self.tableName = tableName
            self.tableIsSet = True
            self.model.setTable(self.tableName)
            self.model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)
            self.queryStr = 'SELECT * FROM %s' % self.tableName
            return True
        else:
            return False
        


    def setQuery(self, queryStr = None):
        # Ternary
        self.queryStr = (self.queryStr, queryStr)[queryStr is not None]


    def loadTable(self):

        if self.query.prepare(self.queryStr) == False:
            self.log.logMe(title="Preparing query", text="Error")
            self.log.logMe(title="Dump", text=self.query.lastError().text())
            return False

        if self.query.exec_() == True:
            self.model.setQuery(self.query)
            #self.setLastQuery()
            self.rowCount = self.query.size()
            self.record = self.model.record()



class relationalModel(baseModel):
      """
         Relational model
      """

      def __init__(self, database):
          super(relationalModel, self).__init__(database)
          self.model = QtSql.QSqlRelationalTableModel()
          self.tableIsSet = False
          self.relations = {}


      def setTable(self, tableName):
          """
          Membutuhkan argumen nama tabel dan list 'header'
          yang akan ditampilkan sebagai 'fieldname' dalam widgetmu.
          """

          if self.db.isValidTable(tableName):
             self.tableName = tableName
             self.tableIsSet = True
          else:
             return False

          self.model.setTable(self.tableName)
          self.model.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit)


      def setRelation(self, fieldPos, tableRef, tableRefIndex, tableRefField):
          """
          Menetapkan relasi antar tabel, yaitu:
          fieldPos; posisi field dalam tabel utama,
          tableRef; tabel yang diacu oleh foreign key
          tableRefIndex; primary key yang diacu dalam tableRef
          tableRefField; field dalam tableRef yang akan digunakan
          """
          # Pastikan bahwa setTable telah dipanggil sebelumnya
          if self.tableIsSet:
              if self.db.isValidTable(tableRef):
                  if (self.db.isValidTableField(tableRef, tableRefIndex) &
                      self.db.isValidTableField(tableRef, tableRefField)):
                      self.relations.__setitem__(fieldPos, QtSql.QSqlRelation(tableRef, tableRefIndex, tableRefField))
                      self.model.setRelation(fieldPos, self.relations[fieldPos])
                      self.log.logMe(title="Set relation", text="'%s' in '%s' done" % (fieldPos, self.tableName))
                      return True


class tableSignal(QtCore.QObject):
      tableLoaded = QtCore.Signal(dict)